/* Name: Item
 * Author: Cameron Hoerig
 * Date: 5-1-12
 * Description: Interface to allow PriorityQueue to execute Draw(),
 *  Update(), destroy(), and mouse functions of any class inheriting the class
 * Revisions: See Subversion logs at http://code.google.com/p/software-engineering-2012/source/list
 */

public class Item {
	final static int COL_NONE = 0;
	final static int COL_LINE = 1;
	final static int COL_CIRCLE = 2;
	
	protected int x;
	protected int y;
	protected int x2;
	protected int y2;
	protected int r;
	protected int colType;
	public Boolean active;
	
	public void draw(){}
	public void update(){}
	public void destroy(){}
	public void collision(Item other){}
	public void leftMousePress(){}
	public void rightMousePress(){}
	public void leftMouseRelease(){}
	public void rightMouseRelease(){}
	
	// Checks for an intersection between two Items
	public static Boolean intersection(Item it1, Item it2) {
		// Line Line Collision
		if (it1.colType == COL_LINE && it2.colType == COL_LINE) {
			return colLineLine(it1,it2);
		}
		// Line Circle Collision
		else if (it1.colType == COL_LINE && it2.colType == COL_CIRCLE) {
			return colLineCircle(it1,it2);	
		}
		// Circle Line Collision
		else if (it1.colType == COL_CIRCLE && it2.colType == COL_LINE) {
			return colLineCircle(it2,it1);	
		}
		// Circle Circle Collision
		else if (it1.colType == COL_CIRCLE && it2.colType == COL_CIRCLE) {
			return colCircleCircle(it1,it2);
		}
		return false;
	}
	
	// Checks for collision between two lines with widths
	private static Boolean colLineLine(Item l1, Item l2) {
		long x1 = l1.x;
		long y1 = l1.y;
		long x2 = l1.x2;
		long y2 = l1.y2;
		long x3 = l2.x;
		long y3 = l2.y;
		long x4 = l2.x2;
		long y4 = l2.y2;
		
		// Check if parallel
		long denom = (x1-x2)*(y3-y4) - (y1-y2)*(x3-x4);
		if (denom == 0) {
			return false;
		}
		long px = ( (x1*y2-y1*x2)*(x3-x4)-(x1-x2)*(x3*y4-y3*x4) ) / denom;
		long py = ( (x1*y2-y1*x2)*(y3-y4)-(y1-y2)*(x3*y4-y3*x4) ) / denom;
		
		// DEBUG
		//Graphics2D gh = Game.getGraphicsHandle();
		//Sprite sprite = Sprite.load("/sprites/blueDot.png");
		//sprite.draw(gh, px, py);
		
		long checkX1, checkY1, checkX2, checkY2;
		
		// Find point on line 1 (or closest end point)
		// Check if on line
		if (( (px >= x1 && px <= x2) || (px >= x2 && px <= x1) )
				&& ( (py >= y1 && py <= y2) || (py >= y2 && py <= y1) )) {
			checkX1 = px;
			checkY1 = py;
		} else { // Set to point closest to
			long dist1 = getDistance(px, py, x1, y1);
			long dist2 = getDistance(px, py, x2, y2);
			if (dist1 <= dist2) {
				checkX1 = x1;
				checkY1 = y1;
			} else {
				checkX1 = x2;
				checkY1 = y2;
			}
		}
		
		// Find point on line 2 (or closest end point)
		// Check if on line
		if (( (px >= x3 && px <= x4) || (px >= x4 && px <= x3) )
				&& ( (py >= y3 && py <= y4) || (py >= y4 && py <= y3) )) {
			checkX2 = px;
			checkY2 = py;
		} else { // Set to point closest to
			long dist1 = getDistance(px, py, x3, y3);
			long dist2 = getDistance(px, py, x4, y4);
			if (dist1 <= dist2) {
				checkX2 = x3;
				checkY2 = y3;
			} else {
				checkX2 = x4;
				checkY2 = y4;
			}
		}
		if ( getDistance(checkX1, checkY1, checkX2, checkY2) <= (l1.r + l2.r) ) {
			return true;
		} else {
			return false;
		}
	}
	
	// Gets distance between two points
	private static long getDistance(long x1, long y1, long x2, long y2) {
		return (long)Math.sqrt(Math.pow(x2-x1,2)+Math.pow(y2-y1,2));
	}
	
	// Checks for collision between a circle and a line with width
	private static Boolean colLineCircle(Item l, Item c) {
		float A = c.x - l.x;//vector from one end point to the test point
		float B = c.y - l.y;
		float C = l.x2 - l.x;//vector from one end point to the other end point
		float D = l.y2 - l.y;
		 
		float dot = A * C + B * D;//some interesting math coming from the geometry of the algorithm
		float len_sq = C * C + D * D;
		float param = dot / len_sq;
		 
		float xx,yy;//the coordinates of the point on the line segment closest to the test point
		 
		//the parameter tells us which point to pick
		//if it is outside 0 to 1 range, we pick one of the endpoints
		//otherwise we pick a point inside the line segment
		if(param < 0)
		{
		    xx = l.x;
		    yy = l.y;
		}
		else if(param > 1)
		{
		    xx = l.x2;
		    yy = l.y2;
		}
		else
		{
		    xx = l.x + param * C;
		    yy = l.y + param * D;
		}
		float  dist = (float)Math.sqrt(Math.pow(c.x - xx,2)+Math.pow(c.y - yy,2));
		return dist < c.r + l.r;
	}
	
	// Checks for a collision between two circles
	private static Boolean colCircleCircle(Item c1, Item c2) {
		float dis = (float) Math.sqrt(Math.pow(c1.x-c2.x,2)+Math.pow(c1.y-c2.y,2));
		return dis <= c1.r + c2.r;
	}
	
	// Returns whether or not the given point is within the viewport
	private boolean inViewport(int theX, int theY) {
		int start = ViewControl.getX() - r;
		int stop = ViewControl.getX() + Game.screenWidth + r;
		if ( start < theX && theX < stop) {
			// Check if within height
			start = ViewControl.getY() - r;
			stop = ViewControl.getY() + Game.screenHeight + r;
			if (start < theY && theY < stop) {
				return true;
			}
		}
		return false;
	}
	
	// Returns whether or not the item is in the current screen view 
	protected boolean onScreen() {
		if (this.colType == COL_CIRCLE) {
			// Check if item is within ViewControl.
			// Check if within width
			return inViewport(x, y);
		} else if (this.colType == COL_LINE) {
			// Check if both points of line are within viewport - need to check bounding box
			return true;
		} else { // Return true for all other items
			return true;
		}
	}
}
